#include <memory>
#include <cstring>

#include "RtpPacket.hpp"

namespace Hi {
RtpPacket *RtpPacket::Clone(const uint8_t *buffer) const {
  auto *ptr = const_cast<uint8_t*>(buffer);
  size_t numBytes = sizeof(Header);
  std::memcpy(ptr, this->GetData(), numBytes);

  // Set header pointer.
  auto *newHeader = reinterpret_cast<Header *>(ptr);

  // Update pointer.
  ptr += numBytes;

  // Copy CSRC list.

  if (this->csrcList != nullptr)
  {
    numBytes = this->header->csrcCount * sizeof(this->header->ssrc);
    std::memcpy(ptr, this->csrcList, numBytes);

    // Update pointer.
    ptr += numBytes;
  }

  // Copy extension header.

  ExtensionHeader *newExtensionHeader{nullptr};

  if (this->extensionHeader != nullptr)
  {
    numBytes = 4 + GetExtensionHeaderLength();
    std::memcpy(ptr, this->extensionHeader, numBytes);

    // Set the header extension pointer.
    newExtensionHeader = reinterpret_cast<ExtensionHeader*>(ptr);

    // Update pointer.
    ptr += numBytes;
  }

  // Copy payload.

  uint8_t *newPayload{nullptr};

  if (this->payload != nullptr)
  {
    numBytes = GetPayloadLength();
    std::memcpy(ptr, this->payload, numBytes);

    // Set the payload pointer.
    newPayload = ptr;

    // Update pointer.
    ptr += numBytes;
  }

  // Copy payload padding.

  if (this->payloadPadding != 0u)
  {
    *(ptr + static_cast<size_t>(this->payloadPadding) - 1) =
        this->payloadPadding;
    ptr += size_t{this->payloadPadding};
  }

  // MS_ASSERT(static_cast<size_t>(ptr - buffer) == this->size,  "ptr - buffer == this->size");

  // Create the new RtpPacket instance and return it.
  auto packet = new RtpPacket(newHeader, newExtensionHeader, newPayload,
                    this->payloadLength, this->payloadPadding, this->size);

  // Clone the extension map.
  // packet->extensionMap = this->extensionMap;

  // packet->payloadDescriptorHandler = this->payloadDescriptorHandler;

  return packet;
}

RtpPacket *RtpPacket::Clone(const uint8_t *buffer, const std::function<void(uint8_t*)>& releaseFun) const {
	auto packet = this->Clone(buffer);
	if (nullptr == packet) {
		return nullptr;
	}
	
	packet->SetUserRelease(releaseFun);
	packet->satelliteMemory = (uint8_t*)buffer;
	return packet;
}

void RtpPacket::SetUserRelease(const std::function<void(uint8_t*)>& releaseFun) {
	this->releaseFunc = releaseFun;
	this->usingUserReleaseFun = true;
}
}
